原生类型

primitive-types.md


commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70

Rust 有一系列被认为是“原生”的类型。这意味着它们是内建在语言中的。Rust被构建为在标准库中也提供了一些建立在这些类型之上的有用的类型,不过它们也大部分是原生的。

布尔型

Rust 有一个内建的布尔类型,叫做bool。它有两个值,truefalse

  1. let x = true;
  2. let y: bool = false;

布尔型通常用在if语句中。

你可以在标准库文档中找到更多关于bool的文档。

char

char类型代表一个单独的 Unicode 字符的值。你可以用单引号(')创建char

  1. let x = 'x';
  2. let two_hearts = '?';

不像其它语言,这意味着Rust的char并不是 1 个字节,而是 4 个。

你可以在标准库文档中找到更多关于char的文档。

数字类型

Rust有一些分类的大量数字类型:有符号和无符号,定长和变长,浮点和整型。

这些类型包含两部分:分类,和大小。例如,u16是一个拥有 16 位大小的无符号类型。更多字节让你拥有更大的数字。

如果一个数字常量没有推断它类型的条件,它采用默认类型:

  1. let x = 42; // `x` has type `i32`.
  2. let y = 1.0; // `y` has type `f64`.

这里有一个不同数字类型的列表,以及它们在标准库中的文档:

让我们按分类重温一遍:

有符号和无符号

整型有两种变体:有符号和无符号。为了理解它们的区别,让我们考虑一个 4 比特大小的数字。一个有符号,4 比特数字你可以储存-8+7的数字。有符号数采用“二进制补码”表示。一个无符号 4 比特的数字,因为它不需要储存负数,可以出储存0+15的数字。

无符号类型使用u作为他们的分类,而有符号类型使用ii代表“integer”。所以u8是一个 8 位无符号数字,而i8是一个 8 位有符号数字。

固定大小类型

固定大小类型在其表现中有特定数量的位。有效的位大小是8163264。那么,u32是无符号的,32 位整型,而i64是有符号,64 位整型。

可变大小类型

Rust 也提供了依赖底层机器指针大小的类型。这些类型拥有“size”分类,并有有符号和无符号变体。它有两个类型:isizeusize

浮点类型

Rust 也有两个浮点类型:f32f64。它们对应 IEEE-754 单精度和双精度浮点数。

数组

像很多编程语言一样,Rust有用来表示数据序列的列表类型。最基本的是数组,一个定长相同类型的元素列表。数组默认是不可变的。

  1. let a = [1, 2, 3]; // a: [i32; 3]
  2. let mut m = [1, 2, 3]; // m: [i32; 3]

数组的类型是[T; N]。我们会在泛型部分的时候讨论这个T标记。N是一个编译时常量,代表数组的长度。

有一个可以将数组中每一个元素初始化为相同值的简写。在这个例子中,a的每个元素都被初始化为0

  1. let a = [0; 20]; // a: [i32; 20]

你可以用a.len()来获取数组a的元素数量:

  1. let a = [1, 2, 3];
  2. println!("a has {} elements", a.len());

你可以用下标subscript notation)来访问特定的元素:

  1. let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3]
  2. println!("The second name is: {}", names[1]);

就跟大部分编程语言一个样,下标从0开始,所以第一个元素是names[0],第二个是names[1]。上面的例子会打印出The second name is: Brian。如果你尝试使用一个不在数组中的下标,你会得到一个错误:数组访问会在运行时进行边界检查。这种不适当的访问是其它系统编程语言中很多bug的根源。

你可以在标准库文档中找到更多关于array的文档。

切片(Slices)

一个切片slice)是一个数组的引用(或者“视图”)。它有利于安全,有效的访问数组的一部分而不用进行拷贝。比如,你可能只想要引用读入到内存的文件中的一行。原理上,片段并不是直接创建的,而是引用一个已经存在的变量。片段有预定义的长度,可以是可变也可以是不可变的。

在底层,slice 代表一个指向数据开始的指针和一个长度。

切片语法(Slicing syntax)

你可以用一个&[]的组合从多种数据类型创建一个切片。&表明切片类似于引用,这个我们会在本部分的后面详细介绍。带有一个范围的[],允许你定义切片的长度:

  1. let a = [0, 1, 2, 3, 4];
  2. let complete = &a[..]; // A slice containing all of the elements in `a`.
  3. let middle = &a[1..4]; // A slice of `a`: only the elements `1`, `2`, and `3`.

片段拥有&[T]类型。当我们涉及到泛型时会讨论这个T

你可以在标准库文档中找到更多关于slices的文档。

str

Rust的str类型是最原始的字符串类型。作为一个不定长类型,它本身并不是非常有用,不过当它用在引用后是就有用了,例如&str。如你所见,我们到时候再讲。

你可以在标准库文档中找到更多关于str的文档。

元组(Tuples)

元组(tuples)是固定大小的有序列表。如下:

  1. let x = (1, "hello");

这是一个长度为 2 的元组,由括号和逗号组成。下面也是同样的元组,不过注明了数据类型:

  1. let x: (i32, &str) = (1, "hello");

如你所见,元组的类型跟元组看起来很像,只不过类型取代的值的位置。细心的读者可能会注意到元组是异质的:这个元组中有一个i32和一个&str。在系统编程语言中,字符串要比其它语言中来的复杂。现在,可以认为&str是一个字符串片段string slice),我们马上会讲到它。

你可以把一个元组赋值给另一个,如果它们包含相同的类型和数量。当元组有相同的长度时它们有相同的数量。

  1. let mut x = (1, 2); // x: (i32, i32)
  2. let y = (2, 3); // y: (i32, i32)
  3. x = y;

你可以通过一个解构letdestructuring let)访问元组中的字段。下面是一个例子:

  1. let (x, y, z) = (1, 2, 3);
  2. println!("x is {}", x);

还记得之前我曾经说过let语句的左侧远比一个赋值绑定强大吗?这就是证据。我们可以在let左侧写一个模式,如果它能匹配右侧的话,我们可以一次写多个绑定。这种情况下,let“解构”或“拆开”了元组,并分成了三个绑定。

这个模式是很强大的,我们后面会经常看到它。

你可以一个逗号来消除一个单元素元组和一个括号中的值的歧义:

  1. (0,); // single-element tuple
  2. (0); // zero in parentheses

元组索引(Tuple Indexing)

你也可以用索引语法访问一个元组的字段:

  1. let tuple = (1, 2, 3);
  2. let x = tuple.0;
  3. let y = tuple.1;
  4. let z = tuple.2;
  5. println!("x is {}", x);

就像数组索引,它从0开始,不过也不像数组索引,它使用.,而不是[]

你可以在标准库文档中找到更多关于tuple的文档。

函数

函数也有一个类型!它们看起来像这样:

  1. fn foo(x: i32) -> i32 { x }
  2. let x: fn(i32) -> i32 = foo;

在这个例子中,x是一个“函数指针”,指向一个获取一个i32参数并返回一个i32值的函数。